package com.wiscamp.ninechapters.problems.application.services;

import com.wiscamp.ninechapters.problems.application.assemblers.ProblemAssembler;
import com.wiscamp.ninechapters.problems.application.assemblers.ProblemCommandConverter;
import com.wiscamp.ninechapters.problems.application.dto.commands.problems.*;
import com.wiscamp.ninechapters.problems.application.dto.vo.problems.ProblemVO;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemRepository;
import com.wiscamp.ninechapters.problems.domain.services.ProblemService;
import cube.ddd.application.BaseAppService;
import cube.ddd.application.command.DeleteCommand;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.utils.AppServiceUtils;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service
public class ProblemAppService extends BaseAppService {

    private final ProblemCommandConverter problemCommandConverter;

    private final ProblemRepository problemRepository;

    private final ProblemService problemService;

    public ProblemAppService(ProblemCommandConverter problemCommandConverter, ProblemRepository problemRepository, ProblemService problemService) {
        this.problemCommandConverter = problemCommandConverter;
        this.problemRepository = problemRepository;
        this.problemService = problemService;
    }

    @HandleServiceException
    public ProblemVO getProblem(long problemId) {
        return AppServiceUtils.getById(problemId, problemRepository::getProblem,
                ProblemAssembler.Instance::toProblemVO);
    }

    @HandleServiceException
    public PagedObjects<ProblemVO> getProblemsByQuery(@NotNull PagedQuery query) {
        var pagedEntities = problemRepository.getProblemsByQuery(query);
        return AppServiceUtils.convertPagedObjects(pagedEntities, ProblemAssembler.Instance::toProblemVO);
    }

    /*@HandleServiceException
    public Collection<TagNameVO> getTagNamesByProblem(long problemId) {
        var tagNames = problemRepository.getTagNamesByProblem(problemId);
        return CollectionHelper.convertToArrayList(tagNames, ProblemAssembler.Instance::toTagNameVO);
    }*/

    @HandleServiceException
    public ProblemVO createProblem(@NotNull CreateProblemCommand command) {
        command.setOperatorId(getCurrentUserId());
        switch (command.getProblemMode()) {
            case SIMPLE -> {
                var createCommand1 = problemCommandConverter.covertCreateSimpleProblemCommand(command);
                return createSimpleProblem(createCommand1);
            }
            case COMPOSITE -> {
                var createCommand2 = problemCommandConverter.covertCreateCompositeProblemCommand(command);
                return createCompositeProblem(createCommand2);
            }
            case HYBRID -> {
                var createCommand3 = problemCommandConverter.covertCreateHybridProblemCommand(command);
                return createHybridProblem(createCommand3);
            }
        }
        return null;
    }

    @HandleServiceException
    public ProblemVO updateProblem(@NotNull UpdateProblemCommand command) {
        command.setOperatorId(getCurrentUserId());
        var problemMode = problemCommandConverter.getProblemMode(command.getProblemId());
        if (problemMode == null) return null;

        switch (problemMode) {
            case SIMPLE -> {
                var updateCommand1 = problemCommandConverter.covertUpdateSimpleProblemCommand(command);
                return updateSimpleProblem(updateCommand1);
            }
            case COMPOSITE -> {
                var updateCommand2 = problemCommandConverter.covertUpdateCompositeProblemCommand(command);
                return updateCompositeProblem(updateCommand2);
            }
            case HYBRID -> {
                var updateCommand3 = problemCommandConverter.covertUpdateHybridProblemCommand(command);
                return updateHybridProblem(updateCommand3);
            }
        }
        return null;
    }

    @HandleServiceException
    public ProblemVO createSimpleProblem(@NotNull CreateSimpleProblemCommand command) {
        command.setOperatorId(getCurrentUserId());
        var simpleProblem = problemCommandConverter.buildSimpleProblemByCreateCommand(command);
        var newProblem = problemService.createProblem(simpleProblem, command.getOperatorId());
        if (Objects.nonNull(newProblem))
            return ProblemAssembler.Instance.toProblemVO(newProblem);
        return null;
    }

    @HandleServiceException
    public ProblemVO updateSimpleProblem(@NotNull UpdateSimpleProblemCommand command) {
        command.setOperatorId(getCurrentUserId());
        var simpleProblem = problemCommandConverter.buildSimpleProblemByUpdateCommand(command);
        var updatedProblem = problemService.updateProblem(simpleProblem, command.getOperatorId());
        if (Objects.nonNull(updatedProblem))
            return ProblemAssembler.Instance.toProblemVO(updatedProblem);
        return null;
    }

    @HandleServiceException
    public ProblemVO createCompositeProblem(@NotNull CreateCompositeProblemCommand command) {
        command.setOperatorId(getCurrentUserId());
        var compositeProblem = problemCommandConverter.buildCompositeProblemByCreateCommand(command);
        var newProblem = problemService.createProblem(compositeProblem, command.getOperatorId());
        if (Objects.nonNull(newProblem))
            return ProblemAssembler.Instance.toProblemVO(newProblem);
        return null;
    }

    @HandleServiceException
    public ProblemVO updateCompositeProblem(@NotNull UpdateCompositeProblemCommand command) {
        command.setOperatorId(getCurrentUserId());
        var compositeProblem = problemCommandConverter.buildCompositeProblemByUpdateCommand(command);
        var updatedProblem = problemService.updateProblem(compositeProblem, command.getOperatorId());
        if (Objects.nonNull(updatedProblem))
            return ProblemAssembler.Instance.toProblemVO(updatedProblem);
        return null;
    }

    @HandleServiceException
    public ProblemVO createHybridProblem(@NotNull CreateHybridProblemCommand command) {
        command.setOperatorId(getCurrentUserId());
        var hybridProblem = problemCommandConverter.buildHybridProblemByCreateCommand(command);
        var newProblem = problemService.createProblem(hybridProblem, command.getOperatorId());
        if (Objects.nonNull(newProblem))
            return ProblemAssembler.Instance.toProblemVO(newProblem);
        return null;
    }

    @HandleServiceException
    public ProblemVO updateHybridProblem(@NotNull UpdateHybridProblemCommand command) {
        command.setOperatorId(getCurrentUserId());
        var hybridProblem = problemCommandConverter.buildHybridProblemByUpdateCommand(command);
        var updatedProblem = problemService.updateProblem(hybridProblem, command.getOperatorId());
        if (Objects.nonNull(updatedProblem))
            return ProblemAssembler.Instance.toProblemVO(updatedProblem);
        return null;
    }

    @HandleServiceException
    public boolean deleteProblem(@NotNull DeleteCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.delete(command.getId(), command.getOperatorId(), problemService::deleteProblem);
    }
}
